package com.sina.shop.dao;


import com.sina.shop.core.BaseDao;
import com.sina.shop.core.domain.BaseCriteria;
import com.sina.shop.core.exception.client.DataException;
import com.sina.shop.core.exception.server.NotUniqueException;
import com.sina.shop.core.exception.server.ResourceExistsException;
import com.sina.shop.core.exception.server.ShopDataAccessException;
import com.sina.shop.reflect.SinaReflectionUtils;
import com.sina.shop.util.ResultCode;
import org.hibernate.HibernateException;
import org.hibernate.query.Query;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.orm.hibernate5.HibernateTemplate;
import org.springframework.util.Assert;

import javax.persistence.NoResultException;
import javax.persistence.Tuple;
import javax.persistence.TupleElement;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaDelete;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import javax.persistence.metamodel.SingularAttribute;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigInteger;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * 段浩杰   2017-09-13-10:59
 */
public abstract class HibernateDaoSupport implements BaseDao {
    protected final Logger LOG = LoggerFactory.getLogger(this.getClass());


    /**
     * classToMap 以 class 为 key 一个不可变的 map(以 class 中的属性为 key ,setter 为 value 的只读 map) 为 value
     * 这个 map 用于做查询结果的 wrapper
     */
    private static final ConcurrentMap<Class<?>, Map<String, Method>> classToMap = new ConcurrentHashMap<>();


    public abstract HibernateTemplate getTemplate();


    protected void applyNamedParameterToQuery(Query<?> queryObject, String paramName, Object value)
            throws HibernateException {

        if (value instanceof Collection) {
            queryObject.setParameterList(paramName, (Collection<?>) value);
        } else if (value instanceof Object[]) {
            queryObject.setParameterList(paramName, (Object[]) value);
        } else {
            queryObject.setParameter(paramName, value);
        }
    }


    /**
     * 查询 唯一的结果
     *
     * @param <T> 仅支持 实体类和 Object[]
     * @return 或无结果则返回 null
     */
    @SuppressWarnings("unchecked")
    protected <T> T unique(String hql, String[] params, Object[] values) {
        return getTemplate().executeWithNativeSession(session -> {

            Query<?> query = session.createQuery(hql);
            if (params != null) {
                for (int i = 0; i < params.length; i++) {
                    applyNamedParameterToQuery(query, params[i], values[i]);
                }
            }

            T t;
            try {
                t = (T) query.getSingleResult();
            } catch (NoResultException e) {
                t = null;
            }

            return t;
        });
    }


    /**
     * 查询 多行结果
     *
     * @param params 参数名,或为 null 则不设参数,长度与 values 一致
     * @param values 参数值 长度与 params 一致
     * @param first  从 0 起
     * @param max    大于 0
     * @param <T>    返回的结果类型,仅支持 实体类 和 Object[]
     * @return 结果
     */
    @SuppressWarnings("unchecked")
    protected <T> List<T> list(String hql, String[] params, Object[] values, int first, int max) {
        return getTemplate().executeWithNativeSession(
                session -> {
                    Query<T> query = session.createQuery(hql);
                    if (params != null) {
                        for (int i = 0; i < params.length; i++) {
                            applyNamedParameterToQuery(query, params[i], values[i]);
                        }
                    }
                    if (first >= 0) {
                        query.setFirstResult(first);
                    }
                    if (max > 0) {
                        query.setMaxResults(max);
                    }

                    return query.getResultList();
                }
        );
    }


    /**
     * @return tuple 或 null
     * @see #listForTuple(String, String[], Object[], int, int)
     */
    protected final Tuple uniqueForTuple(String hql, String[] params, Object[] values) {
        return getTemplate().executeWithNativeSession(session -> {
            Query<Tuple> query = session.createQuery(hql, Tuple.class);
            if (params != null) {
                for (int i = 0; i < params.length; i++) {
                    applyNamedParameterToQuery(query, params[i], values[i]);
                }
            }
            Tuple tuple;
            try {
                tuple = query.getSingleResult();
            } catch (NoResultException e) {
                tuple = null;
            }
            return tuple;
        });
    }

    /**
     * @return T 或 null
     * @see #listForBeanWrapper(String, String[], Object[], int, int, Class)
     */
    protected final <T> T uniqueForBeanWrapper(String hql, String[] params, Object[] values, Class<T> wrapperClass) {
        Tuple tuple = uniqueForTuple(hql, params, values);
        T t = null;
        if (tuple != null) {
            t = wrapperToBeanFromTuple(tuple, wrapperClass, null);
        }
        return t;
    }

    /**
     * @return map 或 null
     * @see #listForMapWrapper(String, String[], Object[], int, int, Class)
     */
    protected final Map<String, Object> uniqueForMapWrapper(String hql, String[] params, Object[] values, Class<?> mapClass) {
        Tuple tuple = uniqueForTuple(hql, params, values);
        Map<String, Object> map = null;
        if (tuple != null) {
            map = wrapperToMapFromTuple(tuple, mapClass);
        }
        return map;
    }

    /**
     * @return map 或 null
     * @see #listForListWrapper(String, String[], Object[], int, int, Class)
     */
    protected final List<Object> uniqueForListWrapper(String hql, String[] params, Object[] values, Class<?> listClass) {
        Tuple tuple = uniqueForTuple(hql, params, values);
        List<Object> list = null;
        if (tuple != null) {
            list = wrapperToListFromTuple(tuple, listClass);
        }
        return list;
    }


    /**
     * 查询 多行结果,每行结果以 Tuple 返回
     *
     * @param params 参数名,或为 null 则不设参数,长度与 values 一致
     * @param values 参数值 长度与 params 一致
     * @param first  从 0 起
     * @param max    大于 0
     * @return 结果列表
     */
    protected final List<Tuple> listForTuple(String hql, String[] params, Object[] values
            , int first, int max) {

        return getTemplate().executeWithNativeSession(session -> {

            Query<Tuple> query = session.createQuery(hql, Tuple.class);

            if (params != null) {
                for (int i = 0; i < params.length; i++) {
                    applyNamedParameterToQuery(query, params[i], values[i]);
                }
            }
            if (first >= 0) {
                query.setFirstResult(first);
            }
            if (max > 0) {
                query.setMaxResults(max);
            }

            return query.getResultList();
        });
    }


    /**
     * 查询 多行结果,将每一行用 bean 包装并返回相应 bean 的列表
     *
     * @param params 参数名,或为 null 则不设参数,长度与 values 一致
     * @param values 参数值 长度与 params 一致
     * @param first  从 0 起
     * @param max    大于 0
     * @param <T>    返回的结果类型,仅支持 Java Bean
     * @return 结果
     */
    protected final <T> List<T> listForBeanWrapper(String hql, String[] params, Object[] values
            , int first, int max, Class<T> wrapperClass) {

        List<Tuple> tupleList = listForTuple(hql, params, values, first, max);

        return beanWrapperFromTuple(tupleList, wrapperClass);

    }

    /**
     * 查询 多行结果,将每一行用 map 包装并返回相应 map 的列表
     *
     * @param params 参数名,或为 null 则不设参数,长度与 values 一致
     * @param values 参数值 长度与 params 一致
     * @param first  从 0 起
     * @param max    大于 0
     * @return 结果列表
     */
    protected final List<Map<String, Object>> listForMapWrapper(String hql, String[] params, Object[] values
            , int first, int max, Class<?> wrapperClass) {

        List<Tuple> tupleList = listForTuple(hql, params, values, first, max);

        return mapWrapperFromTuple(tupleList, wrapperClass);
    }


    /**
     * 查询 多行结果,将每一行用 list 包装并返回相应 list 的列表
     *
     * @param params 参数名,或为 null 则不设参数,长度与 values 一致
     * @param values 参数值 长度与 params 一致
     * @param first  从 0 起
     * @param max    大于 0
     * @return 结果列表
     */
    protected final List<List<Object>> listForListWrapper(String hql, String[] params, Object[] values
            , int first, int max, Class<?> wrapperClass) {

        List<Tuple> tupleList = listForTuple(hql, params, values, first, max);

        return listWrapperFromTuple(tupleList, wrapperClass);

    }

    /**
     * 将 tuple 表示的一行结果包装到 wrapperClass 表示的JavaBean的实例中,并返回实例的列表
     *
     * @param tupleList    （not null）
     * @param wrapperClass (not null), JavaBean 的 class
     * @param <T>          <T> 包装类的类型
     * @return 包装类实例 列表(not null)
     */
    protected final <T> List<T> beanWrapperFromTuple(List<Tuple> tupleList, Class<T> wrapperClass) {

        List<T> list = new ArrayList<>(tupleList.size());
        T t;

        Map<String, Method> setterMap = classToMap.get(wrapperClass);

        if (setterMap == null) {

            setterMap = SinaReflectionUtils.extractSetter(wrapperClass);
            classToMap.putIfAbsent(wrapperClass, setterMap);
        }

        for (Tuple tuple : tupleList) {
            t = wrapperToBeanFromTuple(tuple, wrapperClass, setterMap);
            list.add(t);
        }

        return list;
    }


    /**
     * 将 tuple 表示的一行结果包装到 Map 实例中,并返回 Map 的列表
     *
     * @param tupleList （not null）
     * @param mapClass  (not null), 可以是 Map.class 与 可以是 具体类的 class
     * @return map 列表(not null)
     */
    protected final List<Map<String, Object>> mapWrapperFromTuple(List<Tuple> tupleList, Class<?> mapClass) {

        if (!Map.class.isAssignableFrom(mapClass)) {
            throw new RuntimeException(String.format("mapClass[%s] is assignable from", mapClass));
        }

        List<Map<String, Object>> list = new ArrayList<>(tupleList.size());

        Map<String, Object> map;

        for (Tuple tuple : tupleList) {
            map = wrapperToMapFromTuple(tuple, mapClass);
            list.add(map);
        }
        return list;
    }

    /**
     * 将 tuple 表示的一行结果包装到 list 实例中,并返回map的列表
     *
     * @param tupleList （not null）
     * @param listClass (not null), 可以是 List.class 与 可以是 具体类的 class
     * @return list 列表(not null)
     */
    protected final List<List<Object>> listWrapperFromTuple(List<Tuple> tupleList, Class<?> listClass) {

        if (!List.class.isAssignableFrom(listClass)) {
            throw new RuntimeException(String.format("listClass[%s] is assignable from", listClass));
        }

        List<List<Object>> list = new ArrayList<>(tupleList.size());

        List<Object> rowList;

        for (Tuple tuple : tupleList) {
            rowList = wrapperToListFromTuple(tuple, listClass);
            list.add(rowList);
        }
        return list;
    }


    /**
     * 将 tuple 表示的一行结果包装到 wrapperClass 表示的类的实例中,并返回实例.
     *
     * @param tuple        （not null）
     * @param wrapperClass (not null),包装类 的 class
     * @param setterMap    wrapperClass 的所有 setter 方法,map 的 key 为相应的结果别名
     *                     ,或为 null 则将通过反射得到 setter
     * @param <T>          包装类的类型
     * @return 包装类实例
     * @throws RuntimeException 没有找到某个属性相应的setter，或不可访问.
     */
    protected final <T> T wrapperToBeanFromTuple(Tuple tuple, Class<T> wrapperClass, Map<String, Method> setterMap)
            throws RuntimeException {

        if (setterMap == null) {

            setterMap = classToMap.get(wrapperClass);

            if (setterMap == null) {

                setterMap = SinaReflectionUtils.extractSetter(wrapperClass);
                classToMap.putIfAbsent(wrapperClass, setterMap);
            }

        }

        T t = newWrapper(wrapperClass);

        Method setter;

        for (TupleElement<?> e : tuple.getElements()) {

            setter = setterMap.get(e.getAlias());
            if (setter == null) {
                throw new RuntimeException(String.format("can't find setter of property[%s].", e.getAlias()));
            }
            setWrapper(t, setter, tuple.get(e));
        }
        return t;
    }


    /**
     * 将 tuple 表示的一行结果包装到 List 实例中,并返回实例.
     *
     * @param tuple     （not null）
     * @param listClass (not null), List.class 或具体子类的 class
     * @return list 实例
     * @throws RuntimeException List 子类不可实例化,没有无参构造函数
     */
    protected final List<Object> wrapperToListFromTuple(Tuple tuple, Class<?> listClass) {
        List<Object> list;

        List<TupleElement<?>> elementList = tuple.getElements();

        list = createList(elementList.size(), listClass);

        for (TupleElement<?> e : elementList) {
            list.add(
                    tuple.get(e)
            );
        }
        return list;

    }

    /**
     * 将 tuple 表示的一行结果包装到 Map 实例中,并返回实例.
     *
     * @param tuple    （not null）
     * @param mapClass (not null), Map.class 或具体子类的 class
     * @return map 实例
     * @throws RuntimeException Map 子类不可实例化,没有无参构造函数
     */
    protected final Map<String, Object> wrapperToMapFromTuple(Tuple tuple, Class<?> mapClass) {
        Map<String, Object> map;

        List<TupleElement<?>> elementList = tuple.getElements();

        map = createMap(
                (int) (elementList.size() / 0.75f)
                , mapClass);

        for (TupleElement<?> e : elementList) {
            map.put(e.getAlias()
                    , tuple.get(e)
            );
        }

        return map;
    }

    private List<Object> createList(int initialCapacity, Class<?> listClass) {
        List<Object> list;
        if (List.class.equals(listClass) || ArrayList.class.equals(listClass)) {
            list = new ArrayList<>(initialCapacity);
        } else if (LinkedList.class.equals(listClass)) {
            list = new LinkedList<>();
        } else {
            try {
                list = (List<Object>) newWrapper(listClass);
            } catch (Exception e) {
                throw new RuntimeException(String.format("class[%s] can't instantiate", listClass), e);
            }
        }
        return list;
    }


    protected Map<String, Object> createMap(int initialCapacity, Class<?> mapClass) {
        Map<String, Object> map;
        if (Map.class.equals(mapClass) || HashMap.class.equals(mapClass)) {
            map = new HashMap<>(initialCapacity);
        } else if (LinkedHashMap.class.equals(mapClass)) {
            map = new LinkedHashMap<>(initialCapacity);
        } else {
            try {
                map = (Map<String, Object>) newWrapper(mapClass);
            } catch (RuntimeException e) {
                throw new RuntimeException(String.format("class[%s] can't instantiate", mapClass), e);
            }
        }
        return map;
    }


    /**
     * @throws RuntimeException - wrapperClass 不能实例化时抛出
     */
    protected final <T> T newWrapper(Class<T> wrapperClass) throws RuntimeException {
        try {
            return wrapperClass.newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 通过反射将 将属性值 propertyValue 设置入 包装类实例 wrapper
     *
     * @param wrapper       包装类实例 (not null)
     * @param setter        setter 方法(not null) 注意如果 setter 不是 propertyValue 对应的 setter 将抛出异常
     * @param propertyValue 要设置进 wrapper 的属性值,或 null
     * @throws RuntimeException - 1.方法不是 public 的,2.propertyValue 与 setter 不对应
     */
    protected final void setWrapper(Object wrapper, Method setter, Object propertyValue) throws RuntimeException {
        try {
            setter.invoke(wrapper, propertyValue);
        } catch (IllegalAccessException | InvocationTargetException e) {
            throw new RuntimeException(e);
        } catch (Exception e) {
            LOG.error("", e);
            throw new RuntimeException(e);
        }
    }


    @Override
    public void saveBatch(Collection<?> entities) {
        int count = 0;
        for (Object entity : entities) {
            save(entity);
            count++;
            if (count % BATCH == 0) {
                flush();
                clear();
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void flushAndClear() {
        getTemplate().flush();
        getTemplate().clear();
    }

    protected final <T> SingularAttribute<? super T, ?> getSingularIdAttr(Root<T> root) {
        Class<?> idClass = root.getModel().getIdType().getJavaType();
        return root.getModel().getId(idClass);
    }

    /**
     * 将 整数number{@link Integer} or {@link Long} {@link BigInteger}转换成 Integer
     *
     * @param number 整数, 若为 null 则方法返回 0
     * @throws IllegalArgumentException - number 不是 {@link Integer} or {@link Long}
     */
    protected final Integer toInteger(Object number) throws IllegalArgumentException {
        Integer result;
        if (number == null) {
            result = 0;
        } else if (number instanceof Integer) {
            result = (Integer) number;
        } else if (number instanceof Long) {
            result = ((Long) number).intValue();
        } else if (number instanceof BigInteger) {
            result = ((BigInteger) number).intValue();
        } else {
            throw new IllegalArgumentException(String.format(
                    "number isn't %s or %s ", Integer.class.getName()
                    , Long.class.getName()));
        }
        return result;
    }

    /**
     * 将 整数number{@link Integer} or {@link Long} or {@link BigInteger}转换成 {@link Long}
     *
     * @param number 整数, 若为 null 则方法返回 0
     * @throws IllegalArgumentException - number 不是 {@link Integer} or {@link Long} or {@link BigInteger}
     */
    protected final Long toLong(Object number) throws IllegalArgumentException {
        Long result;
        if (number == null) {
            result = 0L;
        } else if (number instanceof Integer) {
            result = ((Integer) number).longValue();
        } else if (number instanceof Long) {
            result = (Long) number;
        } else if (number instanceof BigInteger) {
            result = ((BigInteger) number).longValue();
        } else {
            throw new IllegalArgumentException(String.format(
                    "number isn't %s or %s ", Integer.class.getName()
                    , Long.class.getName()));
        }
        return result;
    }


    @SuppressWarnings("unchecked")
    public <T> List<T> listWithCommon(Class<T> clazz, int first, int max) {
        return getTemplate().executeWithNativeSession(session -> {

            CriteriaBuilder builder = session.getCriteriaBuilder();
            CriteriaQuery<T> criteriaQuery = builder.createQuery(clazz);
            Root<T> root = criteriaQuery.from(clazz);
            criteriaQuery.select(root);
            Class<?> idType = root.getModel().getIdType().getJavaType();
            criteriaQuery.orderBy(
                    builder.desc(
                            root.get(root.getModel().getId(idType))
                    )
            );
            Query<T> query = session.createQuery(criteriaQuery);

            if (first >= 0) {
                query.setFirstResult(first);
            }
            if (max > 0) {
                query.setMaxResults(max);
            }
            return query.getResultList();
        });
    }

    @Override
    public <T> List<T> list(Class<T> clazz, BaseCriteria criteria) {
        return getTemplate().executeWithNativeSession(session -> {

            CriteriaBuilder builder = session.getCriteriaBuilder();
            CriteriaQuery<T> criteriaQuery = builder.createQuery(clazz);
            Root<T> root = criteriaQuery.from(clazz);

            SingularAttribute<? super T, ?> idAttr = getSingularIdAttr(root);

            criteriaQuery.select(root);

            criteriaQuery.orderBy(
                    builder.desc(
                            root.get(idAttr)
                    )
            );

            Query<T> query = session.createQuery(criteriaQuery);

            final int first = (criteria.getPage() - 1) * criteria.getRows();
            if (first >= 0) {
                query.setFirstResult(first);
            }
            if (criteria.getRows() > 0) {
                query.setMaxResults(criteria.getRows());
            }
            return query.getResultList();
        });
    }


    @Override
    public <T> long count(Class<T> clazz, BaseCriteria criteria) {
        return getTemplate().executeWithNativeSession(session -> {

            CriteriaBuilder builder = session.getCriteriaBuilder();
            CriteriaQuery<Long> criteriaQuery = builder.createQuery(Long.class);
            Root<T> root = criteriaQuery.from(clazz);

            SingularAttribute<? super T, ?> idAttr = getSingularIdAttr(root);

            criteriaQuery.select(builder.count(root.get(idAttr)));

            Query<Long> query = session.createQuery(criteriaQuery);

            Long count;

            try {
                count = query.getSingleResult();
            } catch (NoResultException e) {
                count = 0L;
            }
            return count;

        });
    }


    @Override
    public <T> T getWithException(Class<T> entityClass, Serializable id) throws DataException {
        if (id == null) {
            throw new DataException(ResultCode.dataError, "id 是必需的");
        }
        T t = get(entityClass, id);
        if (t == null) {
            throw new DataException(ResultCode.dataError, String.format("id[%s] 表示的数据不存在", id));
        }
        return t;
    }


    @Override
    public <T> boolean isExists(Class<T> entityClass, Serializable id) {
        Assert.notNull(entityClass, "entityClass is required.");
        return getTemplate().executeWithNativeSession(session -> {

            CriteriaBuilder builder = session.getCriteriaBuilder();
            CriteriaQuery<Object> criteriaQuery = builder.createQuery();
            Root<T> root = criteriaQuery.from(entityClass);

            Class<?> idClass = root.getModel().getIdType().getJavaType();
            SingularAttribute<? super T, ?> idAttr = root.getModel().getId(idClass);

            criteriaQuery.select(root.get(idAttr));

            criteriaQuery.where(
                    builder.equal(root.get(idAttr), id)
            );
            Query<?> query = session.createQuery(criteriaQuery);

            boolean hasResult;
            try {
                hasResult = query.getSingleResult() != null;
            } catch (NoResultException e) {
                System.out.println("sinonyi");
                hasResult = false;
            }
            return hasResult;
        });
    }

    @Override
    public <T> boolean isExistsByUnique(Class<T> entityClass, String propName, Serializable uniqueProp)
            throws ShopDataAccessException {
        Assert.notNull(entityClass, "entityClass is required.");
        Assert.notNull(propName, "propName is required.");
        return getTemplate().executeWithNativeSession(session -> {

            try {
                CriteriaBuilder builder = session.getCriteriaBuilder();
                CriteriaQuery<Object> criteriaQuery = builder.createQuery();
                Root<T> root = criteriaQuery.from(entityClass);

                Class<?> idClass = root.getModel().getIdType().getJavaType();
                SingularAttribute<? super T, ?> idAttr = root.getModel().getId(idClass);

                criteriaQuery.select(root.get(idAttr));

                criteriaQuery.where(
                        builder.equal(root.get(propName), uniqueProp)
                );
                Query<?> query = session.createQuery(criteriaQuery);
                query.setMaxResults(1);
                boolean hasResult;
                try {
                    hasResult = query.getSingleResult() != null;
                } catch (NoResultException e) {
                    hasResult = false;
                }
                return hasResult;
            } catch (DataAccessException e) {
                throw new ShopDataAccessException(ResultCode.dataAccessError, e.getMessage(), e);
            }
        });
    }

    @Override
    public void isExistsWithException(Class<?> entityClass, Serializable id) throws ResourceExistsException {

        if (id != null && isExists(entityClass, id)) {
            throw new ResourceExistsException(ResultCode.dataExists, String.format("数据[id=%s]已存在", id));
        }
    }

    @Override
    public void clear() {
        getTemplate().clear();
    }

    @Override
    public Serializable save(Object entity) {
        return getTemplate().save(entity);
    }

    @Override
    public void persist(Object entity) {
        getTemplate().persist(entity);
    }

    @Override
    public void saveOrUpdate(Object entity) {
        getTemplate().saveOrUpdate(entity);
    }

    @Override
    public void update(Object entity) {
        getTemplate().update(entity);
    }

    @Override
    public void delete(Object entity) {
        getTemplate().delete(entity);
    }


    @Override
    public <T> boolean deleteOne(Class<T> entityClass, Serializable id) {
        return getTemplate().executeWithNativeSession(session -> {

            CriteriaBuilder builder = session.getCriteriaBuilder();
            CriteriaDelete<T> criteriaQuery = builder.createCriteriaDelete(entityClass);

            Root<T> root = criteriaQuery.from(entityClass);

            Class<?> idClass = root.getModel().getIdType().getJavaType();
            SingularAttribute<? super T, ?> idAttr = root.getModel().getId(idClass);


            criteriaQuery.where(builder.equal(root.get(idAttr), id));
            Query<?> query = session.createQuery(criteriaQuery);

            return query.executeUpdate() > 0;

        });
    }


    @Override
    public <T> int deleteCollection(Class<T> entityClass, Collection<? extends Serializable> ids) {
        return getTemplate().executeWithNativeSession(session -> {

            CriteriaBuilder builder = session.getCriteriaBuilder();
            CriteriaDelete<T> criteriaQuery = builder.createCriteriaDelete(entityClass);

            Root<T> root = criteriaQuery.from(entityClass);

            Class<?> idClass = root.getModel().getIdType().getJavaType();
            SingularAttribute<? super T, ?> idAttr = root.getModel().getId(idClass);

            criteriaQuery.where(root.get(idAttr).in(ids));
            Query<?> query = session.createQuery(criteriaQuery);

            return query.executeUpdate();

        });
    }

    @Override
    public <T> T get(Class<T> entityClass, Serializable id) {
        return getTemplate().get(entityClass, id);
    }

    @Override
    public <T> T getByUnique(Class<T> entityClass, String uniquePropName, Serializable uniqueProp)
            throws ShopDataAccessException {
        Assert.notNull(entityClass, "entityClass required");
        Assert.notNull(uniquePropName, "uniqueProp required");
        return getTemplate().executeWithNativeSession(session -> {

            try {

                CriteriaBuilder builder = session.getCriteriaBuilder();
                CriteriaQuery<T> criteriaQuery = builder.createQuery(entityClass);
                Root<T> root = criteriaQuery.from(entityClass);

                criteriaQuery.select(root);

                criteriaQuery.where(
                        builder.equal(root.get(uniquePropName), uniqueProp)
                );

                Query<T> query = session.createQuery(criteriaQuery);
                // 最大为2
                query.setMaxResults(2);

                List<T> list;
                list = query.getResultList();

                T t;

                if (list.isEmpty()) {
                    t = null;
                } else if (list.size() > 1) {
                    throw new NotUniqueException(ResultCode.dataAccessError
                            , String.format("uniquePropName[%s] and uniqueProp[%s] query result size gt 1"
                            , uniquePropName, uniqueProp));
                } else {
                    t = list.get(0);
                }
                return t;

            } catch (DataAccessException e) {
                throw new ShopDataAccessException(ResultCode.dataAccessError, e.getMessage(), e);
            }
        });
    }

    @Override
    public <T> T load(Class<T> entityClass, Serializable id) {
        return getTemplate().load(entityClass, id);
    }

    @Override
    public void flush() {
        getTemplate().flush();
    }

    @Override
    public void evict(Object entity) {
        getTemplate().evict(entity);
    }

    @Override
    public void refresh(Object entity) {
        getTemplate().refresh(entity);
    }
}
